﻿// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: May 5 2010 

#region

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Web.UI;
using System.Xml;

#endregion

namespace Salient.Web
{
    ///<summary>
    /// A helper class that decodes, deserializes the '__ViewState' hidden field
    /// of an ASP.Net in to an object and then XML serializes the object to provide
    /// a navigable map of the view state and control state.
    ///</summary>
    public class ViewStateParser
    {
        #region Fields

        private readonly XmlDocument _controlState;

        private readonly string _text;

        private readonly object _value;

        private readonly XmlDocument _viewState;

        #endregion

        #region Constructors

        ///<summary>
        ///</summary>
        ///<param name="fieldValue"></param>
        public ViewStateParser(string fieldValue)
        {
            _text = fieldValue;

            (_viewState = new XmlDocument())
                .AppendChild(_viewState.CreateElement("viewstate"));

            (_controlState = new XmlDocument())
                .AppendChild(_controlState.CreateElement("controlstate"));

            _value = new LosFormatter().Deserialize(_text);

            XmlSerializeValue(_viewState, _controlState, _viewState.DocumentElement, _value);
        }

        #endregion

        #region Properties

        ///<summary>
        ///</summary>
        public XmlDocument ControlState
        {
            get { return _controlState; }
        }

        ///<summary>
        /// The unencoded __ViewState
        ///</summary>
        public string Text
        {
            get { return _text; }
        }


        ///<summary>
        /// The ViewState object
        ///</summary>
        public object Value
        {
            get { return _value; }
        }


        ///<summary>
        /// 
        ///</summary>
        public XmlDocument ViewState
        {
            get { return _viewState; }
        }

        #endregion

        #region Private Methods

        private static void XmlSerializeValue(XmlDocument viewState, XmlDocument controlState, XmlNode parentNode, object value)
        {
            if (value != null)
            {
                string typename = value.GetType().Name;

                if (typename == "HybridDictionary")
                {
                    XmlElement childNode = controlState.CreateElement(typename);

                    HybridDictionary hybridDictionary = (HybridDictionary) value;

                    if (controlState.DocumentElement != null)
                    {
                        controlState.DocumentElement.AppendChild(childNode);

                        foreach (object item in hybridDictionary)
                        {
                            XmlSerializeValue(controlState, controlState, childNode, item);
                        }
                    }
                }
                else
                {
                    XmlElement childNode = viewState.CreateElement(typename);

                    parentNode.AppendChild(childNode);

                    switch (typename)
                    {
                        case "Triplet":
                            Triplet triplet = (Triplet) value;

                            XmlSerializeValue(viewState, controlState, childNode, triplet.First);
                            XmlSerializeValue(viewState, controlState, childNode, triplet.Second);
                            XmlSerializeValue(viewState, controlState, childNode, triplet.Third);
                            break;

                        case "Pair":
                            Pair pair = (Pair) value;
                            XmlSerializeValue(viewState, controlState, childNode, pair.First);
                            XmlSerializeValue(viewState, controlState, childNode, pair.Second);
                            break;

                        case "ArrayList":
                            foreach (object item in (ArrayList) value)
                            {
                                XmlSerializeValue(viewState, controlState, childNode, item);
                            }
                            break;

                        case "Array":
                            foreach (object item in (Array) value)
                            {
                                XmlSerializeValue(viewState, controlState, childNode, item);
                            }
                            break;

                        case "DictionaryEntry":
                            DictionaryEntry dictionaryEntry = (DictionaryEntry) value;
                            XmlSerializeValue(viewState, controlState, childNode, dictionaryEntry.Key);
                            XmlSerializeValue(viewState, controlState, childNode, dictionaryEntry.Value);
                            break;

                        case "IndexedString":
                            childNode.InnerText = ((IndexedString) value).Value;
                            break;

                        default:
                            childNode.InnerText = value.ToString();
                            break;
                    }
                }
            }
        }

        #endregion
    }
}